//========================================================//
// Thread Library                                         //
// Author: Krechetov Anton aka kae   kae@gorodok.net      //
// NSU FIT July 2004                                      //
//========================================================//

#ifndef _MULTIPLE_THREADS_CLASSES_AND_TIMER
#define _MULTIPLE_THREADS_CLASSES_AND_TIMER

//#include "C:\\Documents and Settings\\sfaktorovich\\My Documents\\remoteconsole2\\vld.h"

#ifdef _WIN32
#include <Windows.h>//link library: coremain.lib
#else
#include <pthread.h>//link option: -lpthread
#include <unistd.h>
#include <time.h>
#endif

#include <list>
#include "exception.h"

//#ifdef _BAM_DEBUG
#include<iostream>
//#endif

using namespace std;

static const std::cException ExcThreadNA("Operation not allowed");
static const std::cException ExcMutex("Mutex error");
static const std::cException ExcEvent("Event error");
static const std::cException ExcSemaphore("Semaphore error");
static const std::cException ExcThreadUnable("Thread error");

class cMutex
{
private:

#ifdef _WIN32
	CRITICAL_SECTION vCS;
#else
	pthread_mutex_t mutex;
#endif

	bool vState;
	bool vDeleted;
	bool vDeleting;

public:
	cMutex();
	void lock();
	void unlock();
//	bool tryLock();
	bool isLocked() const;
	~cMutex();
};

class cRecursiveMutex
{
private:

#ifdef _WIN32
	cMutex vMainMutex;
#else
	long vState;
	bool vDeleted;
	pthread_mutex_t mutex;
#endif

public:
	cRecursiveMutex();
	~cRecursiveMutex();
	void lock();
	void unlock();
};

class cEvent
{
private:

#ifdef _WIN32
	LONG vCount;
	CRITICAL_SECTION vCS;
	HANDLE vSem;
#else
	pthread_mutex_t vMut;
	pthread_cond_t vCond;
#endif

public:
	cEvent();
	void begin();
	void end();
	void wait();
	void wait(unsigned long msec);
	void signal();
	void broadcast();
	~cEvent();
};

class cSemaphore
{
private:

#ifdef _WIN32
	HANDLE vSem;
#endif

public:
	cSemaphore(long init = 0);
	~cSemaphore();
	void increase(unsigned long value = 1);
	void decrease();
};

class iRunnable
{
public:
	virtual void run() = 0;
	virtual void stop() = 0;
	virtual ~iRunnable()
	{}
};

class cThread : public iRunnable
{
private:

#ifdef _WIN32
	typedef HANDLE handleType;
	static DWORD _stdcall runRunnable(void* param);
	unsigned long int vID;
#else
	typedef pthread_t handleType;
	static void* runRunnable(void* param);
#endif

	iRunnable* vRunner;
	handleType vHandle;
	bool vIsRunning;
	bool vIsStopping;

protected:
	bool isStopping();

public:
	cThread();
	cThread(iRunnable* p):
		vRunner(p),
		vIsRunning(false),
		vIsStopping(false)
	{}
	~cThread();
	void run()
	{}
	void start();
	void stop();
	void terminate();
	void lightTerminate();
	static void sleep(long int ms);
};

class cTimer
{
private:
	struct tjob
	{

#ifdef _WIN32
		typedef unsigned long timetype;
#else
		typedef timespec timetype;
#endif

		timetype first;
		iRunnable* second;
		unsigned long vID;
		bool vOwn;
		tjob(timetype time, iRunnable* job, bool own, unsigned long id):
			first(time),
			second(job),
			vOwn(own),
			vID(id)
		{}
	};

	bool vFinished;
	unsigned long vIDs;
	list<tjob> vJobList;

#ifdef _WIN32
	CRITICAL_SECTION vLock;
	HANDLE vNewJobEvent;
	HANDLE vThread;
	DWORD vID;
	static DWORD _stdcall execJobs(void* par);
#else
	pthread_mutex_t vLock;
	pthread_cond_t vAlert;
	pthread_t vThread;
	static void* execJobs(void* par);
#endif

	void doJobs();

public:
	cTimer();
	~cTimer();
	unsigned long addJob(unsigned long msDelay, iRunnable* job, bool own);
	bool removeJob(unsigned long id);
	void stop();
};

class cTimeout
{
private:

#ifdef _WIN32
	DWORD vTime;
#else
	timespec vTime;
#endif

public:
	int type;
	cTimeout();
	void reset();
	bool elapsed(unsigned long sec, unsigned long msec = 0);
	friend std::ostream& operator<<(std::ostream&, cTimeout&);
};

//#ifdef _BAM_DEBUG
	std::ostream& operator<<(std::ostream&, cTimeout&);
//#endif

#endif

